package org.gfix.parser;


public class ValidatingValidator implements FixMessageValidator
{
	public static final int FIX_VERSION = 8;
	public static final String FIX_VERSION_VALUE = "FIX";
	public static final int FIX_MESSAGE_LENTGH = 9;
	public static final int FIX_MESSAGE_TYPE = 35;
	public static final int FIX_CHECK_SUM  = 10;
	
	private static final Rule[] firstTagRules = new Rule[]{
		new Tag8MustBeFirstRule(),
		new Tag9MustBeSecondRule(),
		new Tag35MustBeThirdRule()
	};
	
	private int length;
	boolean startedMessage;
	private int numberOfTagsSeen;
	private int bytesSeen;
	private int sumOfBytesSeen;
	
	
	
	private ValidatingValidator(){init();}

	@Override
	public FixParseFault validate(int tag, String contents)
	{
		// generate the checksum
		if(tag != FIX_CHECK_SUM)
		{
			// the tag itself
			for(byte b : String.valueOf(tag).getBytes())
			{
				sumOfBytesSeen += b;
			}
			sumOfBytesSeen += FixParser.FIX_CONTENT;
			for(byte b : String.valueOf(contents).getBytes())
			{
				sumOfBytesSeen += b;
			}	
			// the delimiter
			sumOfBytesSeen += FixParser.FIX_DELIMITER;
		}
		
		if(numberOfTagsSeen < firstTagRules.length)
		{
			FixParseFault fault = firstTagRules[numberOfTagsSeen].validate(tag, contents);
			if(fault != null)
			{
				return fault;
			}
		}
		numberOfTagsSeen++;
		switch(tag)
		{
			case FIX_VERSION:
			{
				if(startedMessage)
				{
					return new FixParseFault(tag,"Fix version already supplied",
							-1,contents,FixParseFault.Serverity.ERROR);
				}
				
				startedMessage = true;
				if(contents == null || contents.length() == 0)
				{
					return new FixParseFault(tag,"Fix version must be supplied",
							-1,contents,FixParseFault.Serverity.ERROR);
				}
				else if(!contents.startsWith(FIX_VERSION_VALUE))
				{
					return new FixParseFault(tag,"Fix version does not start with fix",
							-1,contents,FixParseFault.Serverity.WARNING);
				}
				break;
			}
			case FIX_MESSAGE_LENTGH:
			{
				if(length!= -1)
				{
					return new FixParseFault(tag,"Message length already supplied",
							-1,contents,FixParseFault.Serverity.ERROR);
				}
				try
				{
					length = Integer.valueOf(contents);
				}
				catch(NumberFormatException nfe)
				{
					return new FixParseFault(tag,"Message length supplied was not a number",
							-1,contents,FixParseFault.Serverity.ERROR);
				}
				break;
			}
			case FIX_CHECK_SUM:
			{
				//first check the length against the claimed length
				if(bytesSeen != length)
				{
					return new FixParseFault(tag,"Message length supplied was not a correct",
							-1,contents,FixParseFault.Serverity.ERROR);
				}
				// check the checksum
				
				int calculated = sumOfBytesSeen % 256;
				try
				{
					int givenChecksum = Integer.valueOf(contents);
					init();
					if(calculated != givenChecksum)
					{
						return new FixParseFault(tag,"Message checksum supplied was not a the same as the calculated: " + calculated,
								-1,contents,FixParseFault.Serverity.ERROR);
					}
				}
				catch(NumberFormatException nfe)
				{
					return new FixParseFault(tag,"Message checksum supplied was not a number",
							-1,contents,FixParseFault.Serverity.ERROR);
				}
				
				// this message is done reset the sucker;
				init();
				break;
			}
			default:
				bytesSeen += String.valueOf(tag).getBytes().length + contents.getBytes().length + 2;
			break;
			
		}
		return null;
	}

	private void init()
	{
		length = -1;
		startedMessage = false;
		numberOfTagsSeen = 0;
		bytesSeen = 0;
		sumOfBytesSeen = 0;
	}
	
	private static interface Rule
	{
		FixParseFault validate(int tag, String contents);
	}
	
	private static class Tag8MustBeFirstRule implements Rule
	{
		public FixParseFault validate(int tag, String contents)
		{
			if( tag != FIX_VERSION)
			{
				return new FixParseFault(tag,"Fix messages must start with 8=",
					-1,contents,FixParseFault.Serverity.ERROR);
			}
			return null;
		}
	}
	
	private static class Tag9MustBeSecondRule  implements Rule
	{
		public FixParseFault validate(int tag, String contents)
		{
			if( tag != FIX_MESSAGE_LENTGH)
			{
				return new FixParseFault(tag,"Fix messages must contain 9=XX as the second tag",
						-1,contents,FixParseFault.Serverity.WARNING);
			}
			return null;
		}
	}
	
	private static class Tag35MustBeThirdRule  implements Rule
	{
		public FixParseFault validate(int tag, String contents)
		{
			if( tag != FIX_MESSAGE_TYPE)
			{
				return new FixParseFault(tag,"Fix messages must contain 35=XX as the third tag",
						-1,contents,FixParseFault.Serverity.WARNING);
			}
			return null;
		}
	}
	
	public static class Factory implements FixMessageValidatorFactory
	{
		public FixMessageValidator getFixMessageValidator()
		{
			return new ValidatingValidator();
		}
	}
	
	
}
